package userInterface.Panels.CommandArgumentPanels;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.filechooser.FileFilter;

import logging.*;
import settings.Action.*;
import userInterface.imageCapture.*;

/**
 * {@link userInterface.panels.CommandArgumentPanels.PanelCommandArguments PanelCommandArguments} for editing
 * {@link settings.Action.Action#VALIDATE Action.VALIDATE} {@link settings.Command Command} arguments.
 * @author BDS
 */
public class PanelValidateArguments extends PanelCommandArguments
{
	private static final long serialVersionUID = 1L;
	
	private ActionListener validateActionListener;
	
	private GridBagLayout gblValidateArguments;
	
	private JLabel lblValidateTarget;
    private DefaultComboBoxModel<ValidateTarget> validateTargets;
	private JLabel lblValidateTargetData;
	private JComboBox<ValidateTarget> cbxValidateTargets;
	private JFormattedTextField txtName;
	private DefaultComboBoxModel<String> images;
	private JComboBox<String> cbxImages;
	private JButton btnImageBrowse;
	private JButton btnImageCapture;
	private JLabel lblImage;
	private JCheckBox chkInvert;

	/**
	 * Class constructor.
	 */
	public PanelValidateArguments()
	{
        validateActionListener = new ValidateActionListener();
        
	    gblValidateArguments = new GridBagLayout();
	    gblValidateArguments.columnWidths = new int[] { 100, 0, 0, 0 } ;
	    gblValidateArguments.columnWeights = new double[] { 0.0, 1.0, 0.0, 0.0 };
	    gblValidateArguments.rowWeights = new double[] { 0.0, 0.0, 1.0, 0.0 };
	    setLayout(gblValidateArguments);

		lblValidateTarget = new JLabel("Target:");
		GridBagConstraints gbc_lblValidateTarget = new GridBagConstraints();
		gbc_lblValidateTarget.anchor = GridBagConstraints.WEST;
        gbc_lblValidateTarget.insets = new Insets(0, 0, 5, 5);
        gbc_lblValidateTarget.gridx = 0;
        gbc_lblValidateTarget.gridy = 0;
        add(lblValidateTarget, gbc_lblValidateTarget);
        
        validateTargets = new DefaultComboBoxModel<ValidateTarget>(ValidateTarget.values());
        cbxValidateTargets = new JComboBox<ValidateTarget>(validateTargets);
        GridBagConstraints gbc_cbxValidateTargets = new GridBagConstraints();
        gbc_cbxValidateTargets.fill = GridBagConstraints.HORIZONTAL;
        gbc_cbxValidateTargets.insets = new Insets(0, 0, 5, 0);
        gbc_cbxValidateTargets.gridwidth = 3;
        gbc_cbxValidateTargets.gridx = 1;
        gbc_cbxValidateTargets.gridy = 0;
        add(cbxValidateTargets, gbc_cbxValidateTargets);
		
		lblValidateTargetData = new JLabel("Name:");
		GridBagConstraints gbc_lblValidateTargetData = new GridBagConstraints();
		gbc_lblValidateTargetData.anchor = GridBagConstraints.WEST;
		gbc_lblValidateTargetData.insets = new Insets(0, 0, 5, 5);
		gbc_lblValidateTargetData.gridx = 0;
		gbc_lblValidateTargetData.gridy = 1;
        add(lblValidateTargetData, gbc_lblValidateTargetData);
		
		txtName = new JFormattedTextField();
		txtName.setEnabled(false);
        GridBagConstraints gbc_txtName = new GridBagConstraints();
        gbc_txtName.fill = GridBagConstraints.HORIZONTAL;
        gbc_txtName.insets = new Insets(0, 0, 5, 0);
        gbc_txtName.gridwidth = 3;
        gbc_txtName.gridx = 1;
        gbc_txtName.gridy = 1;
        add(txtName, gbc_txtName);

		images = new DefaultComboBoxModel<String>();
		cbxImages = new JComboBox<String>(images);
		cbxImages.setVisible(false);
		cbxImages.setActionCommand("Image changed");
		cbxImages.addActionListener(validateActionListener);
		GridBagConstraints gbc_cbxImages = new GridBagConstraints();
		gbc_cbxImages.fill = GridBagConstraints.HORIZONTAL;
        gbc_cbxImages.insets = new Insets(0, 0, 5, 0);
        gbc_cbxImages.gridx = 1;
        gbc_cbxImages.gridy = 1;
        add(cbxImages, gbc_cbxImages);
		
		btnImageBrowse = new JButton();
		btnImageBrowse.setIcon(new ImageIcon(getClass().getResource("/images/browse_tiny.png")));
		btnImageBrowse.setToolTipText("Browse...");
		btnImageBrowse.setVisible(false);
		btnImageBrowse.setActionCommand("Browse");
		btnImageBrowse.addActionListener(validateActionListener);
		GridBagConstraints gbc_btnImageBrowse = new GridBagConstraints();
		gbc_btnImageBrowse.insets = new Insets(0, 0, 5, 0);
		gbc_btnImageBrowse.gridx = 2;
		gbc_btnImageBrowse.gridy = 1;
        add(btnImageBrowse, gbc_btnImageBrowse);
		
		btnImageCapture = new JButton();
		btnImageCapture.setIcon(new ImageIcon(getClass().getResource("/images/browse_tiny.png")));
		btnImageCapture.setToolTipText("Capture...");
		btnImageCapture.setVisible(false);
		btnImageCapture.setActionCommand("Capture");
		btnImageCapture.addActionListener(validateActionListener);
		GridBagConstraints gbc_btnImageCapture = new GridBagConstraints();
		gbc_btnImageCapture.insets = new Insets(0, 0, 5, 0);
		gbc_btnImageCapture.gridx = 3;
		gbc_btnImageCapture.gridy = 1;
        add(btnImageCapture, gbc_btnImageCapture);
        
        lblImage = new JLabel();
        lblImage.setHorizontalAlignment(SwingConstants.CENTER);
        lblImage.setVisible(false);
        GridBagConstraints gbc_lblImage = new GridBagConstraints();
        gbc_lblImage.fill = GridBagConstraints.BOTH;
        gbc_lblImage.insets = new Insets(10, 0, 10, 0);
        gbc_lblImage.gridwidth = 4;
        gbc_lblImage.gridx = 0;
        gbc_lblImage.gridy = 2;
        add(lblImage, gbc_lblImage);
		
		chkInvert = new JCheckBox("Invert");
		chkInvert.setEnabled(false);
        GridBagConstraints gbc_chkInvert = new GridBagConstraints();
        gbc_chkInvert.anchor = GridBagConstraints.WEST;
        gbc_chkInvert.gridwidth = 4;
        gbc_chkInvert.gridx = 0;
        gbc_chkInvert.gridy = 3;
        add(chkInvert, gbc_chkInvert);
		
		loadImageFiles(null);
	}
	
	/**
	 * Sets the enabled state of contained controls.
	 * @param enabled Determines the enabled state of contained controls.
	 */
	@Override
	public void setEnabled(boolean enabled)
	{
        super.setEnabled(enabled);
        
		lblValidateTarget.setEnabled(enabled);
        cbxValidateTargets.setEnabled(enabled);
        
		lblValidateTargetData.setEnabled(enabled);
		txtName.setEnabled(enabled);
		cbxImages.setEnabled(enabled);
		btnImageBrowse.setEnabled(enabled);
		btnImageCapture.setEnabled(enabled);
		lblImage.setEnabled(enabled);
		
		chkInvert.setEnabled(enabled);
		
		if (enabled)
		{
			if (validateTargets.getSelectedItem() == null)
			{
				lblValidateTargetData.setVisible(false);
				txtName.setVisible(false);
				cbxImages.setVisible(false);
				btnImageBrowse.setVisible(false);
				btnImageCapture.setVisible(false);
				lblImage.setVisible(false);
				
                chkInvert.setVisible(false);
			}
			else if (ValidateTarget.fromName(validateTargets.getSelectedItem().toString()) == ValidateTarget.IMAGE)
			{
				lblValidateTargetData.setText("Image:");
				lblValidateTargetData.setVisible(true);
                txtName.setText("");
				txtName.setVisible(false);
				cbxImages.setVisible(true);
				btnImageBrowse.setVisible(true);
				btnImageCapture.setVisible(true);
                lblImage.setVisible(true);
                
				chkInvert.setVisible(true);
			}
			else
			{
				lblValidateTargetData.setText("Name:");
                lblValidateTargetData.setVisible(true);
				txtName.setVisible(true);
				cbxImages.setVisible(false);
				btnImageBrowse.setVisible(false);
				btnImageCapture.setVisible(false);
                lblImage.setVisible(false);
                
				chkInvert.setVisible(true);
			}
		}
	}
	
	/**
	 * Provides the external event processing class references to the contained {@link javax.swing.JComponent JComponents}.
	 * @param actionListener The {@link java.awt.event.ActionListener ActionListener} for {@link javax.swing.JComboBox JComboBoxes}.
	 * @param focusAdapter The {@link java.awt.event.FocusAdapter FocusAdapter} for {@link javax.swing.JFormattedTextField JFormattedTextFields}.
	 * @param keyAdapter The {@link java.awt.event.KeyAdapater KeyAdapater} for {@link javax.swing.JFormattedTextField JFormattedTextFields}.
	 * @param changeListener The {@link javax.swing.event.ChangeListener ChangeListener} for {@link javax.swing.JSpinner JSpinners}.
	 */
	@Override
	public void addActionListeners(ActionListener actionListener, FocusAdapter focusAdapter, KeyAdapter keyAdapter, ChangeListener changeListener)
	{
		cbxValidateTargets.addActionListener(actionListener);
		txtName.addFocusListener(focusAdapter);
		txtName.addKeyListener(keyAdapter);
		cbxImages.addActionListener(actionListener);
		chkInvert.addActionListener(actionListener);
	}
	
	/**
	 * Gets the {@link settings.Action.Action#VALIDATE Action.VALIDATE} {@link settings.Command Command} arguments currently displayed.
	 * @return The {@link settings.Action.Action#VALIDATE Action.VALIDATE} {@link settings.Command Command} arguments currently displayed.
	 */
	@Override
	public List<String> getArguments()
	{ 
		List<String> arguments = new ArrayList<String>();
		
		if (validateTargets.getSelectedItem() != null)
		{
			arguments.add(validateTargets.getSelectedItem().toString());
			
			if (ValidateTarget.fromName(validateTargets.getSelectedItem().toString()) == ValidateTarget.IMAGE)
			{
				if (images.getSelectedItem() != null)
				{
					arguments.add(images.getSelectedItem().toString());
				}
				else
				{
					arguments.add(null);
				}
			}
			else
			{
				arguments.add(txtName.getText());
			}
			
			arguments.add(new Boolean(chkInvert.isSelected()).toString());
		}
		
		return arguments;
	}
	
	/**
	 * <ul>
	 *     <li>Sets the {@link settings.Action.Action#VALIDATE Action.VALIDATE} {@link settings.Command Command} arguments.</li>
	 *     <li>Updates the UI based on the given arguments.</li>
	 * </ul>
	 * @param arguments The {@link settings.Action.Action#VALIDATE Action.VALIDATE} {@link settings.Command Command} arguments used to update the UI.
	 */
	@Override
	public void setArguments(List<String> arguments) 
	{
		if (arguments != null &&
			arguments.size() > 0 && 
			arguments.get(0) != null && 
			!arguments.get(0).isEmpty())
		{
			validateTargets.setSelectedItem(ValidateTarget.fromName(arguments.get(0)));
			
			if (arguments.size() > 1 && arguments.get(1) != null && !arguments.get(1).isEmpty()) 
			{
				if (ValidateTarget.fromName(validateTargets.getSelectedItem().toString()) == ValidateTarget.IMAGE)
				{
					images.setSelectedItem(arguments.get(1));
				}
				else
				{
					txtName.setText(arguments.get(1));
				}
				
				if (arguments.size() > 2 && arguments.get(2) != null && !arguments.get(2).isEmpty()) 
				{
					chkInvert.setSelected(Boolean.parseBoolean(arguments.get(2)));
				}
				else
				{
					chkInvert.setSelected(false);
				}
			}
			else
			{
				txtName.setText("");
				cbxImages.setSelectedIndex(-1);
				chkInvert.setSelected(false);
			}
		}
		else
		{
			cbxValidateTargets.setSelectedIndex(-1);
			txtName.setText("");
			cbxImages.setSelectedIndex(-1);
			chkInvert.setSelected(false);
		}
	}
	
	/**
	 * Loads all image file information from the local image directory.
	 * @param imageToSelect The image to select in the {@link javax.swing.JComboBox JComboBox} after loading is complete.
	 */
	private void loadImageFiles(String imageToSelect)
	{
		try
		{
			File imageDirectory = new File(System.getProperty("user.dir") + "\\Images");
			if (imageDirectory.exists() || imageDirectory.mkdir())
			{
				images.removeAllElements();
				
				if (imageDirectory.listFiles() != null)
				{
					for (File imageFile : imageDirectory.listFiles()) 
					{
						// Ignore the self and parent aliases.
						if (!imageFile.getName().equals(".") &&
							!imageFile.getName().equals("..")) 
						{
							images.addElement(imageFile.getName());
						}
					}
				}
				
				if (imageToSelect != null && !imageToSelect.equals(""))
				{
					images.setSelectedItem(imageToSelect);
				}
			}
		}
		catch (Exception exception)
		{
			Logger.logException(exception);
		}
	}
	
	/**
	 * {@link java.awt.event.ActionListener ActionListener} for {@link userInterface.panels.CommandArgumentPanels.PanelValidateArguments PanelValidateArguments}
	 * {@link javax.swing.JComboBox JComboBoxes} and {@link javax.swing.JButton JButtons}.
	 * @author BDS
	 */
	private class ValidateActionListener implements ActionListener
	{
	    /**
         * Executes specific logic based on the following {@link java.awt.event.ActionEvent ActionEvents}:
         * <ul>
         *     <li>Image changed - Updates the {@link javax.swing.JLabel JLabel} representing the selected image.</li>
         *     <li>Browse - Opens a {@link javax.swing.JFileChooser JFileChooser} for populating the image {@link javax.swing.JComboBox JComboBox}.</li>
         *     <li>Capture - Utilizes {@link userInterface.imageCapture.ImageCapture ImageCapture} to capture a new validation image.</li>
         * </ul>
         * @param actionEvent The triggering {@link java.awt.event.ActionEvent ActionEvent}.
         */
        @Override
		public void actionPerformed(ActionEvent actionEvent)
		{
            if (actionEvent.getActionCommand().equals(cbxImages.getActionCommand()))
            {
                lblImage.setIcon(new ImageIcon(System.getProperty("user.dir") + "\\Images\\" + images.getSelectedItem()));
            }
            else if (actionEvent.getActionCommand().equals(btnImageBrowse.getActionCommand()))
			{
				JFileChooser fileChooser = new JFileChooser(System.getProperty("user.dir"));
				fileChooser.setFileFilter(new ImageCheckFileFilter());
				fileChooser.setAcceptAllFileFilterUsed(false);
				if (fileChooser.showDialog(((JButton) actionEvent.getSource()).getParent(), "Add") == JFileChooser.APPROVE_OPTION)
				{
				    try 
				    {
				    	File destinationFile = new File(System.getProperty("user.dir") + "\\Images\\" + fileChooser.getSelectedFile().getName());				    	
				    	Files.copy(fileChooser.getSelectedFile().toPath(), destinationFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
						loadImageFiles(destinationFile.getName());
				    }
				    catch (IOException exception)
				    {
				    	Logger.logException(exception);
				    }
				}
			}
			else if (actionEvent.getActionCommand().equals(btnImageCapture.getActionCommand()))
			{
				loadImageFiles(ImageCapture.captureImage());
			}
		}

		/**
		 * {@link javax.swing.filechooser.FileFilter FileFilter} for the
		 * {@link userInterface.panels.CommandArgumentPanels.PanelValidateArguments.ValidateActionListener ValidateActionListener}
		 * {@link javax.swing.JFileChooser JFileChooser}.
		 * @author BDS
		 */
		private class ImageCheckFileFilter extends FileFilter
		{
			/**
			 * Accepts only directories or image files (gif, jpg, and png).
			 * @param file The {@link java.io.File File} to validate.
			 * @return <ul>
			 *             <li><code>true</code> if the provided {@link java.io.File File} is a directory or image.</li>
             *             <li><code>false</code> otherwise.</li>
			 *         </ul>
			 */
			@Override
			public boolean accept(File file)
			{
				return file.isDirectory() ||
					   file.getName().matches(".*?\\.gif") ||
					   file.getName().matches(".*?\\.jpg") ||
					   file.getName().matches(".*?\\.png");
			}

			/**
			 * Expresses that only image files are supported.
			 * @return Always returns "Image".
			 */
			@Override
			public String getDescription()
			{
				return "Image";
			}
		}
	}
}
